home *** CD-ROM | disk | FTP | other *** search
Text File | 2007-10-18 | 69.3 KB | 2,095 lines |
- // BEGIN FLOCK GPL
- //
- // Copyright Flock Inc. 2005-2007
- // http://flock.com
- //
- // This file may be used under the terms of of the
- // GNU General Public License Version 2 or later (the "GPL"),
- // http://www.gnu.org/licenses/gpl.html
- //
- // Software distributed under the License is distributed on an "AS IS" basis,
- // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- // for the specific language governing rights and limitations under the
- // License.
- //
- // END FLOCK GPL
-
- const CC = Components.classes;
- const CI = Components.interfaces;
- const CR = Components.results;
-
- Components.utils.import("resource://gre/modules/JSON.jsm");
-
- const FACEBOOK_CID = Components.ID("{81a58e10-4f45-11db-b0de-0800200c9a66}");
- const FACEBOOK_CONTRACTID = "@flock.com/people/facebook;1";
- const FACEBOOK_FAVICON = "http://www.facebook.com/favicon.ico";
- const FACEBOOK_TITLE = "Facebook Web Service";
- const SERVICE_ENABLED_PREF = "flock.service.facebook.enabled";
- const CATEGORY_COMPONENT_NAME = "Facebook JS Component";
- const CATEGORY_ENTRY_NAME = "facebook";
- const FLOCK_RICH_CONTENT_CATEGORY_ENTRY = "flockRichContentHandler";
- const XPATH_WALLTEXT = "wallTextXPath";
-
- const FLOCK_ACCOUNT_UTILS_CONTRACTID = "@flock.com/account-utils;1";
- const FLOCK_ERROR_CONTRACTID = "@flock.com/error;1";
- const FLOCK_PHOTO_ACCOUNT_CONTRACTID = "@flock.com/photo-account;1";
- const FLOCK_PHOTO_ALBUM_CONTRACTID = "@flock.com/photo-album;1";
- const FLOCK_PHOTO_API_MANAGER_CONTRACTID = "@flock.com/photo-api-manager;1?";
- const FLOCK_PHOTO_CONTRACTID = "@flock.com/photo;1";
- const FLOCK_RDNDS_CONTRACTID = "@flock.com/rich-dnd-service;1";
- const FLOCK_SINGLETON_CONTRACTID = "@flock.com/singleton;1";
- const CATEGORY_MANAGER_CONTRACTID = "@mozilla.org/categorymanager;1";
- const HASH_PROPERTY_BAG_CONTRACTID = "@mozilla.org/hash-property-bag;1";
- const JS_SUBSCRIPT_LOADER_CONTRACTID = "@mozilla.org/moz/jssubscript-loader;1";
- const OBSERVER_SERVICE_CONTRACTID = "@mozilla.org/observer-service;1";
-
- const FLOCK_SNOWMAN_URL = "chrome://browser/skin/flock/services/common/no_avatar.png";
- const FAVES_COOP = "chrome://flock/content/common/load-faves-coop.js";
- const FACEBOOK_PROPERTIES = "chrome://flock/locale/services/facebook.properties";
- const PEOPLE_PROPERTIES = "chrome://flock/locale/people/people.properties";
-
- const FACEBOOK_IDENTITY_URN_PREFIX = "urn:flock:identity:facebook:";
-
- const FACEBOOK_SHARE_FLOCK_SUBJECT = "flock.facebook.friendShareFlock.subject";
- const FACEBOOK_SHARE_FLOCK_MESSAGE = "flock.facebook.friendShareFlock.message";
-
- // The first time, only get photos not older than one week
- const MEDIA_INITIAL_FETCH = 7 * 24 * 3600;
- // The delay between two refreshes when the sidebar is closed (in seconds)
- const FACEBOOK_REFRESH_INTERVAL = 1800; // 30 minutes
- // The delay between two refreshes when the sidebar is open (in seconds)
- const FACEBOOK_SHORT_INTERVAL = 300; // 5 minutes
-
- // CONSTANTS FOR PHOTO UPLOADING
- // XXX This is a rough guess. Facebook allows a max of 60 pictures per
- // personal account, at a max file size of 5MB each. Once there are API
- // calls to actually get the space remaining, we should replace this.
- const FACEBOOK_MAX_PHOTO_SPACE = 60 * 5 * 1024 * 1024;
- const FACEBOOK_USED_PHOTO_SPACE = 0;
- // Facebook has a maximum file size of 5MB
- const FACEBOOK_MAX_FILE_SIZE = 5 * 1024 * 1024;
-
- const CHAR_CODE_SPACE = " ".charCodeAt(0);
-
- // ====================================================
- // ========== BEGIN General helper functions ==========
- // ====================================================
-
- var gCompTK;
- function getCompTK() {
- if (!gCompTK) {
- gCompTK = CC[FLOCK_SINGLETON_CONTRACTID]
- .getService(CI.flockISingleton)
- .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
- .wrappedJSObject;
- }
- return gCompTK;
- }
-
- // For use with the scheduler
- var gTimers = [];
-
- // String defaults... may be updated later through Web Detective
- var gStrings = {
- "domains": "facebook.com",
- "userlogin": "http://www.facebook.com/login.php",
- "userprofile": "http://www.facebook.com/profile.php?id=%accountid%"
- };
-
- var _wm = CC["@mozilla.org/appshell/window-mediator;1"]
- .getService(CI.nsIWindowMediator);
-
- function loadSubScript(spec) {
- var loader = CC[JS_SUBSCRIPT_LOADER_CONTRACTID]
- .getService(CI.mozIJSSubScriptLoader);
- var context = {};
- loader.loadSubScript(spec, context);
- return context;
- }
-
- function loadLibraryFromSpec(aSpec) {
- var loader = CC[JS_SUBSCRIPT_LOADER_CONTRACTID]
- .getService(CI.mozIJSSubScriptLoader);
- loader.loadSubScript(aSpec);
- }
-
- loadLibraryFromSpec("chrome://browser/content/flock/photo/photoAPI.js");
- loadLibraryFromSpec("chrome://flock/content/common/flocksafe.js");
-
- function _getIdentityUrn(aAccountId, aUid) {
- var result = FACEBOOK_IDENTITY_URN_PREFIX
- + aAccountId + ":"
- + aUid;
-
- return result;
- }
-
- // A special hack to trigger embedding rich content into a Facebook field.
- // Added as a general helper function as it is used in both FacebookService
- // and FacebookAccount classes.
- function _initKeyupEvent(aTarget) {
- if (!aTarget) {
- return;
- }
-
- var doc = aTarget.ownerDocument;
- if (!(doc instanceof CI.nsIDOMHTMLDocument)) {
- return;
- }
-
- var docView = doc.QueryInterface(CI.nsIDOMDocumentView).defaultView;
- if (!(docView instanceof CI.nsIDOMAbstractView)) {
- return;
- }
-
- // add an extra space as the first of two parts to simulate the keyup event
- aTarget.value += " ";
-
- // Send a key-up event to the field: this triggers a Facebook
- // process which scrapes the field for a link which, if found,
- // will embed the rich content into the field.
- var eventObj = doc.createEvent("KeyEvents");
- eventObj.initKeyEvent("keyup", true, true, docView, false, false, false,
- false, CI.nsIDOMKeyEvent.DOM_VK_SPACE, CHAR_CODE_SPACE);
- aTarget.dispatchEvent(eventObj);
-
- // Remove the space to get back to the original value
- var val = aTarget.value;
- aTarget.value = val.substring(0, val.length - 1);
- }
-
- // Appends the Flock breadcrumb to the given text field
- function _addBreadcrumb(aField) {
- if (aField) {
- var breadcrumb = CC[FLOCK_RDNDS_CONTRACTID]
- .getService(CI.flockIRichDNDService)
- .getBreadcrumb("plain");
- if (breadcrumb) {
- aField.value += breadcrumb;
- }
- }
- }
-
-
- //******************************************
- //* Service Class
- //******************************************
-
- function flockFacebookService() {
- this.obs = CC[OBSERVER_SERVICE_CONTRACTID].getService(CI.nsIObserverService);
- this.acUtils = CC[FLOCK_ACCOUNT_UTILS_CONTRACTID].
- getService(CI.flockIAccountUtils);
-
- this.status = CI.flockIWebService.STATUS_UNKNOWN;
- this.url = "http://www.facebook.com";
- this.api = CC["@flock.com/api/facebook;1"].getService(CI.flockIFacebookAPI).wrappedJSObject;
- this.mIsInitialized = false;
- this.photoList = null;
-
- this._ctk = {
- interfaces: [
- "nsISupports",
- "nsIClassInfo",
- "nsISupportsCString",
- "nsIObserver",
- "flockIWebService",
- "flockIAuthenticateNewAccount",
- "flockISocialWebService",
- "flockIManageableWebService",
- "flockIPollingService",
- "flockIMediaWebService",
- "flockIRichContentDropHandler"
- ],
- shortName: "facebook",
- fullName: "Facebook",
- description: FACEBOOK_TITLE,
- domains: gStrings["domains"],
- favicon: FACEBOOK_FAVICON,
- CID: FACEBOOK_CID,
- contractID: FACEBOOK_CONTRACTID,
- accountClass: flockFacebookAccount,
- needPassword: true
- };
-
- var sbs = CC["@mozilla.org/intl/stringbundle;1"]
- .getService(CI.nsIStringBundleService);
- var bundle = sbs.createBundle(FACEBOOK_PROPERTIES);
-
- this._channels = {
- "special:photosFromFriends": {
- title: bundle.GetStringFromName("flock.facebook.title.friends"),
- supportsSearch: false
- },
- "special:photosOfFriends": {
- title: bundle.GetStringFromName("flock.facebook.title.recent"),
- supportsSearch: false
- },
- "special:photosOfYou": {
- title: bundle.GetStringFromName("flock.facebook.title.you"),
- supportsSearch: false
- }
- };
-
- this._logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
- this._logger.init("facebookService");
- this._profiler = CC["@flock.com/profiler;1"].getService(CI.flockIProfiler);
- this.init();
- }
-
- flockFacebookService.prototype.init =
- function flockFacebookService_init() {
- // Prevent re-entry
- if (this.mIsInitialized) {
- return;
- }
- this.mIsInitialized = true;
- this._logger.info(".init()");
-
- // Determine whether this service has been disabled, and unregister if so.
- this.prefService = CC["@mozilla.org/preferences-service;1"]
- .getService(CI.nsIPrefBranch);
- if (this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
- !this.prefService.getBoolPref(SERVICE_ENABLED_PREF)) {
- this._logger.info(SERVICE_ENABLED_PREF + " is FALSE! Not initializing.");
- var cm = CC[CATEGORY_MANAGER_CONTRACTID].getService(CI.nsICategoryManager);
- cm.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true);
- cm.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true);
- cm.deleteCategoryEntry("flockMediaProvider", CATEGORY_ENTRY_NAME, true);
- cm.deleteCategoryEntry(FLOCK_RICH_CONTENT_CATEGORY_ENTRY,
- CATEGORY_ENTRY_NAME, true);
- return;
- }
-
- this.faves_coop = CC[FLOCK_SINGLETON_CONTRACTID]
- .getService(CI.flockISingleton)
- .getSingleton(FAVES_COOP)
- .wrappedJSObject;
-
- this.account_root = this.faves_coop.accounts_root;
-
- this.fbService = new this.faves_coop.Service(
- "urn:facebook:service", {
- name: "facebook",
- desc: "Facebook"
- });
- this.fbService.serviceId = FACEBOOK_CONTRACTID;
-
- this.urn = this.fbService.id();
-
- // Load Web Detective file
- this.webDetective = this.acUtils.useWebDetective("facebook.xml");
- for (var s in gStrings) {
- gStrings[s] = this.webDetective.getString("facebook", s, gStrings[s]);
- }
- this.fbService.domains = gStrings["domains"];
- this.fbService.loginURL = gStrings["userlogin"];
-
- // Update auth states
- try {
- if (this.webDetective.detectCookies("facebook", "loggedout", null)) {
- this.acUtils.markAllAccountsAsLoggedOut(FACEBOOK_CONTRACTID);
- this.logout();
- }
- } catch (ex) {
- this._logger.error("ERROR updating auth states for Facebook: " + ex);
- }
-
- // On browser restart if a Facebook coop account is still marked as
- // authenticated we also need to reauth the api. We do this by calling
- // login() on the xpcom account obj. However, we have to wait while
- // the Facebook service is finished constructing itself.
- var inst = this;
- var reloginCallback = {
- notify: function reloginCallback_notify(aTimer) {
- inst._logger.debug("reloginCallback.notify()");
- var acctURN = inst.acUtils
- .getFirstAuthenticatedAccountForService(FACEBOOK_CONTRACTID);
-
- if (acctURN) {
- var account = inst.getAccount(acctURN);
- if (account) {
- var reloginListener = {
- onSuccess: function reloginListener_onSuccess(aSubject, aTopic) {
- inst._logger.debug(".init(): reloginListener: onSuccess()");
- },
- onError: function reloginListener_onError(aError) {
- inst._logger.debug(".init(): reloginListener: onError()");
- inst.faves_coop.get(acctURN).isAuthenticated = false;
- }
- };
- account.login(reloginListener);
- }
- }
- }
- };
- var reloginTimer = CC["@mozilla.org/timer;1"].createInstance(CI.nsITimer);
- reloginTimer.initWithCallback(reloginCallback, 100, CI.nsITimer.TYPE_ONE_SHOT);
-
- }
-
- /**
- * The following method defined in nsIObserver are provided by component
- * toolkit:
- *
- * @see nsIObserver#observe
- */
-
- /**
- * @see flockIPollingService#refresh
- */
- flockFacebookService.prototype.refresh =
- function flockFacebookService_refresh(aUrn, aListener) {
- this._logger.debug(".refresh() with aUrn of " + aUrn);
- var refreshItem = this.faves_coop.get(aUrn);
-
- if (refreshItem instanceof this.faves_coop.Account) {
- this._logger.debug("refreshing an Account");
- if (this.api.isLoggedIn) {
- this._refreshAccount(aUrn, aListener);
- } else {
- this._logger.debug("api is not logged in - skipping refresh");
- // If the user is not logged in, return a success without
- // refreshing anything
- aListener.onResult();
- }
- } else if (refreshItem instanceof this.faves_coop.Identity) {
- this._logger.debug("refreshing an Identity");
- aListener.onResult();
- } else {
- throw CR.NS_ERROR_ABORT;
- this._logger.error("can't refresh " + aUrn + " (unsupported type)");
- aListener.onError(null);
- }
- }
-
- flockFacebookService.prototype._refreshAccount =
- function flockFacebookService_refreshAccount(aUrn, aListener) {
- var inst = this;
- var refreshItem = this.faves_coop.get(aUrn);
- var lastUpdate = refreshItem.lastUpdateDate;
- var peopleHash;
- var numberOfAnswers = 0;
-
- function onIndividualSuccess() {
- numberOfAnswers++;
- if (numberOfAnswers >= 3) {
- if (inst.acUtils.isPeopleSidebarOpen()) {
- refreshItem.nextRefresh = new Date(Date.now()
- + FACEBOOK_SHORT_INTERVAL * 1000);
- }
- if (aListener) {
- aListener.onResult();
- }
- }
- }
-
- var mediaFriendsListener = {
- onSuccess: function mfl_onSuccess(aEnum, aStatus) {
- inst._logger.info("Successful getUpdatedMediaFriends");
- aEnum.QueryInterface(CI.nsISimpleEnumerator);
-
- // Get the media information, put it in the peopleHash
- while (aEnum.hasMoreElements()) {
- var bag = aEnum.getNext();
- bag.QueryInterface(CI.nsIPropertyBag);
- uid = bag.getProperty("uid");
- peopleHash[uid].media = {
- count: bag.getProperty("count"),
- latest: bag.getProperty("latest")
- }
- }
-
- // Now that we have all we need, update the RDF
- function myWorker(aShouldYield) {
- var count = 0;
- // ADD or update existing people
- for (uid in peopleHash) {
- count++;
- inst._addPerson(peopleHash[uid], refreshItem);
- if (aShouldYield()) {
- yield;
- }
- }
- // REMOVE locally people removed on the server
- var localEnum = refreshItem.friendsList.children.enumerate();
- while (localEnum.hasMoreElements()) {
- var identity = localEnum.getNext();
- if (!peopleHash[identity.accountId]) {
- inst._logger.info("Friend " + identity.accountId
- + " has been deleted on the server");
- refreshItem.friendsList.children.remove(identity);
- identity.destroy();
- }
- }
- onIndividualSuccess();
- }
- getCompTK().schedule(gTimers, 0.05, 10, myWorker);
- },
- onError: function mfl_onError(aError) {
- inst._logger.error("Error on getUpdatedMediaFriends");
- }
- }
-
- var friendsGetListener = {
- onSuccess: function fgl_onSuccess(aResult, aStatus) {
- inst._logger.info("Success for friendsGetListener");
-
- peopleHash = aResult.wrappedJS;
-
- refreshItem.lastUpdateDate = new Date();
- var lastUpdateStr;
- if (lastUpdate) {
- // Convert milliseconds to seconds then to a string
- lastUpdateStr = new String(parseInt(lastUpdate.getTime() / 1000));
- } else {
- lastUpdateStr = new String(parseInt(Date.now() / 1000)
- - MEDIA_INITIAL_FETCH);
- }
- inst.api.getUpdatedMediaFriends(mediaFriendsListener, lastUpdateStr);
- },
- onError: function fgl_onError() {
- inst._logger.error("Error on gettingFriends")
- }
- };
-
- var usersGetInfoListener = {
- onSuccess: function ugil_onSuccess(aEnum, aStatus) {
- inst._logger.info("Success for usersGetInfo");
- aEnum.QueryInterface(CI.nsISimpleEnumerator);
- if (aEnum.hasMoreElements()) {
- var bag = aEnum.getNext();
- bag.QueryInterface(CI.nsIPropertyBag);
- refreshItem.avatar = bag.getProperty("avatar");
- // We get the full name each refresh since it's possible for the
- // user's full name to change.
- refreshItem.name = bag.getProperty("name");
- refreshItem.statusMessage = bag.getProperty("statusMessage");
- refreshItem.lastProfileUpdate = bag.getProperty("lastProfileUpdate");
-
- onIndividualSuccess();
- }
- },
- onError: function ugil_onError() {
- inst._logger.error("Error on usersGetInfo");
- }
- };
-
- var notificationsGetListener = {
- onSuccess: function ngl_onSuccess(aEnum, aStatus) {
- inst._logger.info("Success for notificatonsGet");
- aEnum.QueryInterface(CI.nsISimpleEnumerator);
- if (aEnum.hasMoreElements()) {
- var bag = aEnum.getNext();
- bag.QueryInterface(CI.nsIPropertyBag);
-
- // if any of the Me notifications have increased in count,
- // light the people button
- if ((refreshItem.accountMessages < bag.getProperty("messages")) ||
- (refreshItem.fbPokes < bag.getProperty("pokes")) ||
- (refreshItem.fbFriendRequests < bag.getProperty("friendRequests")) ||
- (refreshItem.fbGroupInvites < bag.getProperty("groupInvites")) ||
- (refreshItem.fbEventInvites < bag.getProperty("eventInvites")))
- {
- inst._lightPeopleIcon();
- }
-
- // write notifications to RDF
- refreshItem.accountMessages = bag.getProperty("messages");
- refreshItem.fbPokes = bag.getProperty("pokes");
- refreshItem.fbFriendRequests = bag.getProperty("friendRequests");
- refreshItem.fbGroupInvites = bag.getProperty("groupInvites");
- refreshItem.fbEventInvites = bag.getProperty("eventInvites");
- }
-
- onIndividualSuccess();
- },
- onError: function ngl_onError() {
- inst._logger.error("Error on notificatonsGet");
- }
- };
-
- this.api.usersGetInfo(usersGetInfoListener,
- this.api.uid,
- ["name,pic_square,status"]);
- this.api.friendsGet(friendsGetListener);
- this.api.notificationsGet(notificationsGetListener);
- }
-
- flockFacebookService.prototype._incrementMedia =
- function flockFacebookService_incrementMedia(aUid, aCount, aLatest) {
- var currAcctURN = this.acUtils.getFirstAuthenticatedAccountForService(FACEBOOK_CONTRACTID);
- var currAcct = this.faves_coop.get(currAcctURN);
-
- // Update the count on the identity...
- var identityUrn = _getIdentityUrn(currAcct.accountId, aUid);
- identity = this.faves_coop.get(identityUrn);
- identity.unseenMedia += aCount;
-
- // + 10 explaination: when the friend uploads a new photo, Facebook
- // updates the profile update time a few seconds after the photo upload.
- // We want that to show as a "media" update, not as a "profile" update
- if ((aLatest + 10) >= identity.lastUpdate) {
- identity.lastUpdate = aLatest;
- identity.lastUpdateType = "media";
- }
- }
-
-
- flockFacebookService.prototype._addPerson =
- function flockFacebookService_addPerson(aPerson, aCoopAccount) {
- var person = aPerson;
-
- // We include the current accountId in the identity urn to prevent friend
- // collisions if multiple Facebook accounts have the same friend.
- var identityUrn = _getIdentityUrn(aCoopAccount.accountId,
- person.uid);
- var updating = this.faves_coop.Identity.exists(identityUrn);
- var identity;
-
- var lastProfileUpdate = parseInt(person.profile_update_time, 10);
- var statusTime = parseInt(person.status.time, 10);
- var lastUpdate = 0;
- var lastUpdateType;
- if (statusTime > lastProfileUpdate) {
- lastUpdate = statusTime;
- lastUpdateType = "status";
- } else {
- lastUpdate = lastProfileUpdate;
- lastUpdateType = "profile";
- }
- this._logger.debug("Adding person: " + person.uid + " - " + person.name);
-
- if (updating) {
- // update data of the identity coop obj here
- identity = this.faves_coop.get(identityUrn);
- if (lastUpdate > identity.lastUpdate) {
- identity.lastUpdate = lastUpdate;
- identity.lastUpdateType = lastUpdateType;
- identity.name = person.name;
- identity.avatar = person.pic_square;
- identity.statusMessage = person.status.message;
- }
- } else {
- identity = new this.faves_coop.Identity(
- identityUrn,
- {
- name: person.name,
- serviceId: FACEBOOK_CONTRACTID,
- accountId: person.uid,
- avatar: person.pic_square,
- statusMessage: person.status.message,
- lastUpdate: lastUpdate,
- lastUpdateType: lastUpdateType
- }
- );
- aCoopAccount.friendsList.children.add(identity);
- }
-
- if (person.media) {
- this._incrementMedia(person.uid,
- person.media.count,
- person.media.latest);
- }
- }
-
- flockFacebookService.prototype.markAllMediaSeen =
- function flockFacebookService_markAllMediaSeen(aIdentityUrn) {
- var identity = this.faves_coop.get(aIdentityUrn);
- identity.unseenMedia = 0;
- }
-
- // Facebook's HTML sets a maxlength of 160 on the status entry textbox.
- flockFacebookService.prototype.maxStatusLength = 160;
-
- flockFacebookService.prototype._lightPeopleIcon =
- function flockFacebookService_lightPeopleIcon() {
- this._logger.debug("._lightPeopleIcon()");
- this.obs.notifyObservers(null, "new-people-notification", null);
- }
-
- flockFacebookService.prototype._addPhotostream =
- function flockFacebookService_addPhotostream(aPhotoPerson, aCoopAccount) {
- var mediaFavesUrn = "urn:media:favorites";
- var query = new queryHelper();
- var person = aPhotoPerson.QueryInterface(CI.nsIPropertyBag);
- query.user = person.getProperty("uid");
- query.username = person.getProperty("name");
- this._logger.info(".addPhotostream('" + query.username + "')");
- var mediaQueryUrn = mediaFavesUrn + ":facebook:" + query.stringVal;
- var mediaQuery = this.faves_coop.get(mediaQueryUrn);
- if (!mediaQuery) {
- mediaQuery = new this.faves_coop.MediaQuery(
- mediaQueryUrn,
- {
- serviceId: FLOCK_PHOTO_API_MANAGER_CONTRACTID,
- service: this.shortName,
- favicon: FACEBOOK_FAVICON,
- isPollable: true,
- query: query.stringVal,
- name: person.getProperty("name")
- }
- );
- }
- mediaQuery.isTransient = aCoopAccount.isTransient;
- var mediaFaves = this.faves_coop.get(mediaFavesUrn);
- if (!mediaFaves) {
- mediaFaves = new this.faves_coop.Folder(mediaFavesUrn);
- this.faves_coop.favorites_root.children.add(mediaFaves);
- }
- mediaFaves.children.addOnce(mediaQuery);
- }
-
- /**
- * The following attributes and methods defined in flockIWebService are
- * provided by component toolkit:
- *
- * @see flockIWebService#icon
- * @see flockIWebService#needPassword
- * @see flockIWebService#shortName
- * @see flockIWebService#title
- * @see flockIWebService#urn
- * @see flockIWebService#getAccount
- * @see flockIWebService#getAccounts
- * @see flockIWebService#removeAccount
- */
-
- /**
- * @see flockIWebService#addAccountById
- */
- flockFacebookService.prototype.addAccountById =
- function flockFacebookService_addAccountById(aAccountID,
- aIsTransient,
- aListener) {
- this._logger.debug(".addAccountById('"
- + aAccountID + "', "
- + aIsTransient + "...)");
-
- var pw = this.acUtils.getPassword(this.urn + ":" + aAccountID);
- /// @todo: FIX ME - name shouldn't be email address (pw.user) - DP
- var name = (pw) ? pw.user : aAccountID;
-
- var accountUrn = "urn:flock:facebook:account:" + aAccountID;
- var account = new this.faves_coop.Account(
- accountUrn, {
- name: name,
- serviceId: FACEBOOK_CONTRACTID,
- service: this.fbService,
- accountId: aAccountID,
- isPollable: false,
- refreshInterval: FACEBOOK_REFRESH_INTERVAL,
- URL: gStrings["userprofile"].replace("%accountid%", aAccountID),
- isTransient: aIsTransient,
- favicon: FACEBOOK_FAVICON
- });
-
- this.account_root.children.add(account);
- this.USER = account.id();
-
- var friendsListUrn = accountUrn + ":friends";
- var friendsList = new this.faves_coop.FriendsList(
- friendsListUrn,
- {
- account: account
- });
- account.friendsList = friendsList;
-
- // Instantiate account component
- var acct = this.getAccount(account.id());
- if (aListener) {
- aListener.onSuccess(acct, "addAccount");
- }
- return acct;
- }
-
- // BEGIN flockIAuthenticateNewAccount interface
- flockFacebookService.prototype.authenticateNewAccount =
- function flockFacebookService_authenticateNewAccount(aListener)
- {
- this._logger.info("{flockIAuthenticateNewAccount}.authenticateNewAccount(aListener)");
- var wm = CC["@mozilla.org/appshell/window-mediator;1"]
- .getService(CI.nsIWindowMediator);
- var win = wm.getMostRecentWindow('navigator:browser');
- win.openUILinkIn(this.url, "tab");
- }
- // END flockIAuthenticateNewAccount interface
-
- /**
- * The following methods defined in flockIManageableWebService are provided by
- * component toolkit:
- *
- * @see flockIManageableWebService#docRepresentsSuccessfulLogin
- * @see flockIManageableWebService#getAccountIDFromDocument
- * @see flockIManageableWebService#getCredentialsFromForm
- * @see flockIManageableWebService#ownsDocument
- * @see flockIManageableWebService#ownsLoginForm
- */
-
- /**
- * @see flockIManageableWebService#updateAccountStatusFromDocument
- */
- flockFacebookService.prototype.updateAccountStatusFromDocument =
- function flockFacebookService_updateAccountStatusFromDocument(aDocument) {
- this._logger.debug(".updateAccountStatusFromDocument(aDocument)");
- if (this.webDetective.detect("facebook", "loggedout", aDocument, null)) {
- this.acUtils.markAllAccountsAsLoggedOut(FACEBOOK_CONTRACTID);
- this.api.deauthenticate();
- } else if (this.webDetective.detect("facebook",
- "loggedin",
- aDocument,
- null))
- {
- var results = CC[HASH_PROPERTY_BAG_CONTRACTID]
- .createInstance(CI.nsIWritablePropertyBag2);
- if (this.webDetective.detect("facebook",
- "accountinfo",
- aDocument,
- results))
- {
- var acctId = results.getPropertyAsAString("accountid");
- var acctUrn = this.acUtils.getAccountURNById(this.urn, acctId);
- var acct = this.faves_coop.get(acctUrn);
- if (!acct.isAuthenticated) {
- var inst = this;
- var loginListener = {
- onSuccess: function loginListener_onSuccess() {
- inst._logger.info(".updateAccountStatusFromDocument(): "
- + "loginListener: onSuccess()");
- acct.isAuthenticated = true;
- },
- onError: function loginListener_onError(aError) {
- inst._logger.info(".updateAccountStatusFromDocument(): "
- + "loginListener: onError()");
- inst._logger.info(aError ? (aError.errorString) : "No details");
- acct.isAuthenticated = false;
- }
- };
- this.getAccount(acctUrn).login(loginListener);
- }
- }
- }
- }
-
-
-
-
- /**
- * createAlbum
- * @see flockIMediaWebService#createAlbum
- */
- flockFacebookService.prototype.createAlbum =
- function flockFacebookService_createAlbum(aListener, aTitle) {
- // trim white space from front and end of string
- var newTitle = aTitle.replace(/^\s+|\s+$/g, "");
- if (newTitle) {
- var myListener = {
- onSuccess: function listener_onSuccess(aAlbum, aStatus) {
- aListener.onCreateAlbum(aAlbum);
- },
- onError: function listener_onError(aFlockError) {
- aListener.onError(aFlockError);
- }
- }
- this.api.createAlbum(myListener, newTitle);
- } else {
- var error = CC[FLOCK_ERROR_CONTRACTID].createInstance(CI.flockIError);
- error.errorCode = CI.flockIError.PHOTOSERVICE_EMPTY_ALBUMNAME;
- aListener.onError(null, null, error);
- }
- }
-
-
- /**
- * findByUsername
- * @see flockIMediaWebService#findByUsername
- */
- flockFacebookService.prototype.findByUsername =
- function flockFacebookService_findByUsername(aListener, aUsername) {
- // need to call this to trigger mediaObserver
- aListener.onError(null);
- }
-
- /**
- * getAccountStatus
- *
- * This currently returns hardcoded values so the uploader works. This
- * should be replaced by actual calls to the Facebok API, or with values
- * screen-scraped by Web Detective.
- *
- * @see flockIMediaWebService#getAccountStatus
- * @todo Use the Facebook API or Web Detective to correctly obtain this
- * information.
- * @todo Localize the hardcoded strings.
- */
- flockFacebookService.prototype.getAccountStatus =
- function flockFacebookService_getAccountStatus(aListener) {
- var result = CC["@mozilla.org/hash-property-bag;1"]
- .createInstance(CI.nsIWritablePropertyBag2);
-
- result.setPropertyAsAString("maxSpace", FACEBOOK_MAX_PHOTO_SPACE);
- result.setPropertyAsAString("usedSpace", FACEBOOK_USED_PHOTO_SPACE);
- result.setPropertyAsAString("maxFileSize", FACEBOOK_MAX_FILE_SIZE);
- // This is a key, not something displayed to the user. Don't l10n this!
- result.setPropertyAsAString("usageUnits", "bytes");
- result.setPropertyAsBool("isPremium", true); // Facebook has no "premium" level.
-
- aListener.onSuccess(result, "");
- }
-
- /**
- * getAlbums
- * @see flockIMediaWebService#getAlbums
- *
- * aUsername is the FB uid
- */
- flockFacebookService.prototype.getAlbums =
- function flockFacebookService_getAlbums(aListener, aUsername) {
- this._logger.info(".getAlbums(aListener, " + aUsername + ")");
- var myListener = {
- onSuccess: function listener_onSuccess(aEnumResults, aStatus) {
- aListener.onGetAlbumsResult(aEnumResults);
- },
- onError: function listener_onError(aSubject, aTopic, aFlockError) {
- aListener.onError(aFlockError);
- }
- };
-
- this.api.getAlbums(myListener, aUsername);
- }
-
- /**
- * getAuthPerson
- * @see flockIMediaWebService#getAuthPerson
- */
- flockFacebookService.prototype.getAuthPerson =
- function flockFacebookService_getAuthPerson () {
- try {
- var person = {};
- person.id = this.api.uid;
- // XXX Localize these
- person.fullname = "Unknown";
- person.username = "Unknown";
- person.service = this;
- return person;
- } catch (ex) {
- return null;
- }
- }
-
-
- /**
- * getContacts
- * @see flockIMediaWebService#getContacts
- * @todo Implement this, or handle the error more gracefully.
- * @throws NS_ERROR_NOT_IMPLEMENTED
- */
- flockFacebookService.prototype.getContacts =
- function flockFacebookService_getContacts(aListener) {
- throw CR.NS_ERROR_NOT_IMPLEMENTED;
- }
-
- /**
- * getMostRecentPhotoForList
- * @see flockIMediaWebService#getMostRecentPhotoForList
- * @todo Implement this, or handle the error more gracefully.
- * @throws NS_ERROR_NOT_IMPLEMENTED
- */
- flockFacebookService.prototype.getMostRecentPhotoForList =
- function flockFacebookService_getMostRecentPhotoForList(aListener,
- aEnumerator) {
- throw CR.NS_ERROR_NOT_IMPLEMENTED;
- }
-
- /**
- * getPhoto
- * @see flockIMediaWebService#getPhoto
- * @todo Implement this, or handle the error more gracefully.
- * @throws NS_ERROR_NOT_IMPLEMENTED
- */
- flockFacebookService.prototype.getPhoto =
- function flockFacebookService_getPhoto(aListener, aPhotoId) {
- throw CR.NS_ERROR_NOT_IMPLEMENTED;
- }
-
- /**
- * getPhotoFromRDFNode
- * @see flockIMediaWebService#getPhotoFromRDFNode
- */
- flockFacebookService.prototype.getPhotoFromRDFNode =
- function flockFacebookService_getPhotoFromRDFNode(aRdfId) {
- var newPhoto = this.api.createPhoto();
- var coopPhoto = this.faves_coop.get(aRdfId);
- newPhoto.webPageUrl = coopPhoto.URL;
- newPhoto.thumbnail = coopPhoto.thumbnail;
- newPhoto.midSizePhoto = coopPhoto.midSizePhoto;
- newPhoto.largeSizePhoto = coopPhoto.largeSizePhoto;
- newPhoto.username = coopPhoto.username;
- newPhoto.userid = coopPhoto.userid;
- newPhoto.title = coopPhoto.name;
- newPhoto.id = coopPhoto.photoid;
- newPhoto.icon = coopPhoto.favicon;
- newPhoto.uploadDate = coopPhoto.datevalue;
- newPhoto.is_public = coopPhoto.is_public;
- newPhoto.is_video = coopPhoto.is_video;
-
- return newPhoto;
- }
-
- /**
- * logout
- * @see flockIWebService#logout
- */
- flockFacebookService.prototype.logout =
- function flockFacebookService_logout() {
- this._logger.debug(".logout()");
- this.api.deauthenticate();
- this.acUtils.removeCookies(this.webDetective.getSessionCookies("facebook"));
- this.USER = null;
- }
-
- /**
- * search
- * @see flockIMediaWebService#search
- */
- flockFacebookService.prototype.search =
- function flockFacebookService_search(aListener, aQueryString, aCount, aPage) {
- this._logger.debug("Attempting to run search query: " + aQueryString);
- var inst = this;
- if (aPage > 1) {
- // Implement a pseudo-pagination since Facebook does not support pagination
- getPhotosDetails(aCount, aPage);
- return;
- }
-
- var aQuery = new queryHelper(aQueryString);
-
- function createEnum(myarray) {
- myarray.reverse();
- return {
- QueryInterface: function (iid) {
- if (iid.equals(Components.interfaces.nsISimpleEnumerator)) {
- return this;
- }
- throw Components.results.NS_ERROR_NO_INTERFACE;
- },
- hasMoreElements: function() {
- return (myarray.length>0);
- },
- getNext: function() {
- return myarray.shift();
- }
- }
- }
-
- function getPhotosDetails(aCount, aPage) {
- var photos = [];
- if (inst.photoList) {
- var owners = [];
-
- var sbs = CC["@mozilla.org/intl/stringbundle;1"].
- getService(CI.nsIStringBundleService);
- var bundleUrl = "chrome://flock/locale/photo/mediabar.properties";
- var bundle = sbs.createBundle(bundleUrl);
-
- var pageEnd = aCount * aPage;
- if (pageEnd > inst.photoList.length) {
- pageEnd = inst.photoList.length;
- }
- for (var i = aCount * (aPage - 1); i < pageEnd; i++) {
- var photo = inst.photoList[i];
- var id = photo.getElementsByTagName('pid')[0]
- .firstChild
- .nodeValue;
- var title;
- if (photo.getElementsByTagName('caption')[0].firstChild) {
- title = photo.getElementsByTagName('caption')[0]
- .firstChild
- .nodeValue;
- } else {
- title = bundle.GetStringFromName("flock.media.tooltip.untitled");
- }
- var owner = photo.getElementsByTagName('owner')[0]
- .firstChild.nodeValue;
- var date_upload = photo.getElementsByTagName('created')[0]
- .firstChild
- .nodeValue;
- var square_url = photo.getElementsByTagName('src')[0]
- .firstChild
- .nodeValue;
- var small_url = photo.getElementsByTagName('src_small')[0]
- .firstChild
- .nodeValue;
- var big_url = photo.getElementsByTagName('src_big')[0]
- .firstChild
- .nodeValue;
- var page_url = photo.getElementsByTagName('link')[0]
- .firstChild
- .nodeValue;
- var newPhoto = inst.api.createPhoto();
- newPhoto.webPageUrl = page_url;
- newPhoto.thumbnail = small_url;
- newPhoto.midSizePhoto = square_url;
- newPhoto.largeSizePhoto = big_url;
- newPhoto.userid = owner;
- newPhoto.title = title;
- newPhoto.id = id;
- newPhoto.uploadDate = parseInt(date_upload) * 1000;
- photos.push(newPhoto);
- owners.push(owner);
- }
- if (pageEnd == inst.photoList.length) {
- inst.photoList = null;
- }
- }
-
- // Exit this whole thing early if we got nothing back.
- if (photos.length == 0) {
- aListener.onSearchResult(createEnum(photos));
- return;
- }
-
- // We sort the array to ease de-dupeing by placing duplicate numbers
- // next to each other.
- owners = owners.sort();
- var ownerList = "";
- var prev = 0;
- for each (var owner in owners) {
- if (owner == prev) {
- continue;
- }
- if (ownerList != "") {
- ownerList += ",";
- }
- ownerList += owner;
- prev = owner;
- }
-
- if (ownerList != "") {
- var detailsListener = {
- onSuccess: function listener_onSuccess(aEnum) {
- var ownersObject = {};
- aEnum = aEnum.QueryInterface(CI.nsISimpleEnumerator);
- while (aEnum.hasMoreElements()) {
- var person = aEnum.getNext();
- person = person.QueryInterface(CI.nsIWritablePropertyBag2);
- ownersObject[person.getProperty("uid")] = person;
- }
-
- for (var j = 0; j < photos.length; j++) {
- var owner = ownersObject[photos[j].userid];
- owner = owner.QueryInterface(CI.nsIWritablePropertyBag2);
- photos[j].username = owner.getProperty("name");
- var avatar = owner.getProperty("avatar");
- if (avatar == "") {
- avatar = FLOCK_SNOWMAN_URL;
- }
- photos[j].icon = avatar;
- }
- photos.sort(function (a,b) {
- return a.uploadDate - b.uploadDate;
- });
- aListener.onSearchResult(createEnum(photos));
- }
- };
- inst.api.usersGetInfo(detailsListener,
- ownerList,
- ["name,pic_square"]);
- }
- }
-
- var fqlListener = {
- onSuccess: function listener_onSuccess(aXml, aStatus) {
- inst.photoList = aXml.getElementsByTagName("photo");
- getPhotosDetails(aCount, 1);
- },
- onError: function listener_onError(aSubject, aTopic, aFlockError) {
- aListener.onError(aFlockError);
- }
- };
-
- var qry = "SELECT pid, owner, src_small, src_big, src, link, caption, "
- + "created FROM photo ";
- if (aQuery.user) {
- // Query to get all photos by person with name defined in aQuery.search.
- // Could be more than one person's sets of photos.
- if (aQuery.album) {
- qry += "WHERE aid IN (" + aQuery.album + ")";
- } else {
- qry += "WHERE aid IN ("
- + "SELECT aid FROM album WHERE owner = '"
- + aQuery.user
- + "')";
- }
- this._logger.debug("Performing FQLQuery: " + qry);
- this.api.doFQLQuery(fqlListener, qry);
-
- return;
- } else if (aQuery.searchunique) {
- // Query to get all photos tagged with uid specified in aQuery.searchunique
- qry += "WHERE pid IN ("
- + "SELECT pid FROM photo_tag WHERE subject = '"
- + aQuery.searchunique
- + "')";
- this._logger.debug("Performing FQLQuery: " + qry);
- this.api.doFQLQuery(fqlListener, qry);
-
- return;
- } else if (aQuery.special) {
- switch (aQuery.special) {
- case "photosFromFriends":
- qry += "WHERE aid IN ("
- + "SELECT aid FROM album WHERE owner IN ("
- + "SELECT uid2 FROM friend WHERE uid1 = "
- + this.api.uid
- + ")) "
- + "AND created > now() - 2592000"; // One month (in seconds)
- break;
- case "photosOfFriends":
- qry += "WHERE pid IN ("
- + "SELECT pid FROM photo_tag WHERE subject IN ("
- + "SELECT uid2 FROM friend WHERE uid1 = "
- + this.api.uid
- + ")) "
- + "AND created > now() - 2592000"; // One month (in seconds)
- break;
- case "photosOfYou":
- qry += "WHERE pid IN ("
- + "SELECT pid FROM photo_tag WHERE subject="
- + this.api.uid
- + ")";
- break;
- }
- this._logger.debug("Performing FQLQuery: " + qry);
- this.api.doFQLQuery(fqlListener, qry);
-
- return;
- } else {
- // if user enters a search with illegal characters
- // (any non alpha-num or space)
- // then return right away with no results
- if (aQuery.search.match(/[^a-zA-Z0-9 ]/)) {
- aListener.onSearchResult(createEnum([]));
- return;
- }
- }
-
- function _normalizeQuery(input) {
- var output;
- var in_quotes = false;
-
- // strip leading spaces
- while (input.length && input[0] == " ") {
- // chop off the first character
- input = input.substr(1);
- }
-
- // Convert to lowercase for the wildcard search
- input = input.toLowerCase();
-
- // add leading plus
- output = "+";
- while (input.length) {
- if (input[0] == "\"") {
- if (in_quotes) {
- in_quotes = false;
- } else {
- in_quotes = true;
- }
- }
-
- // escape special chars
- if ("&|!(){}[]^~*?:\\".indexOf(input[0]) >= 0) {
- output = output + "\\";
- }
-
- output = output + input[0];
-
- if (!in_quotes && input[0] == " " && input.length > 1) {
- // put a plus before every word
- output = output + "+";
- }
-
- input = input.substr(1);
- }
-
- // remove trailing spaces
- var removed_spaces = false;
- while (output.length > 0 && output[output.length - 1] == " ") {
- output = output.substr(0, output.length - 1);
- removed_spaces = true;
- }
-
- // if we're in a quoted string we should close that
- if (in_quotes) {
- output = output + "\"";
- }
-
- // if theres an incomplete word
- if ((!removed_spaces) && (output[output.length - 1] != "\"")) {
- // search for partial matches
- output = output + "*";
- }
-
- return output;
- }
-
- // Query to get all photos tagged with name defined in aQuery.search.
- // We use Lucene to get partial matches.
- var searchService = CC["@flock.com/lucene/flockLucene;1"]
- .getService(CI.flockILucene);
- var inst = this;
-
- var searchListener = {
- onSearchComplete:
- function searchListener_onSearchComplete(aNumResults, aResults) {
- inst._logger.debug("onSearchComplete: " + aNumResults);
- var uidArray = [];
- while (aResults.hasMoreElements()) {
- var result = aResults.getNext().QueryInterface(CI.flockILuceneResult);
- var uid = result.URI.split(":").pop();
- uidArray.push(uid);
- }
-
- var uidArrayStr = "(" + uidArray.join(",") + ")";
- var fullnameQuery = "(SELECT uid "
- + "FROM user "
- + "WHERE name = '"
- + aQuery.search
- + "')"
- qry += "WHERE pid IN ("
- + "SELECT pid FROM photo_tag "
- + "WHERE subject IN " + fullnameQuery + " "
- + "OR subject IN " + uidArrayStr
- + ")";
-
- inst._logger.debug("Performing FQLQuery: " + qry);
- inst.api.doFQLQuery(fqlListener, qry);
- }
- }
-
- searchService.search(_normalizeQuery(aQuery.search),
- "identity",
- 100, searchListener);
- }
-
- /**
- * supportsFeature
- * @see flockIMediaWebService#supportsFeature
- */
- flockFacebookService.prototype.supportsFeature =
- function flockFacebookService_supportsFeature(aFeature) {
- var supports = {
- albumCreation: true,
- contacts: true,
- filename: false,
- privacy: false,
- tags: false,
- notes: true,
- title: false
- };
- return supports[aFeature];
- }
-
- /**
- * supportsSearch
- * @see flockIMediaWebService#supportsSearch
- */
- flockFacebookService.prototype.supportsSearch =
- function flockFacebookService_supportsSearch(aQueryString) {
- return false;
- }
-
- /**
- * upload2
- * @see flockIMediaWebService#upload
- */
- flockFacebookService.prototype.upload =
- function flockFacebookService_upload(aListener, aUpload, aFilename) {
- var params = CC[HASH_PROPERTY_BAG_CONTRACTID]
- .createInstance(CI.nsIWritablePropertyBag2);
- params.setPropertyAsAString("description", aUpload.description);
-
- this.api.uploadPhotosViaUploader(aListener, aFilename, params, aUpload);
- }
-
- /**
- * The following methods defined in flockIMediaWebService are handled by
- * component toolkit:
- *
- * @see flockIMediaWebService#checkIsStreamUrl
- * @see flockIMediaWebService#handlesMediaStream
- * @see flockIMediaWebService#getMediaQueryFromURL
- */
-
- /**
- * decorateForMedia
- * @see flockIMediaWebService#decorateForMedia
- */
- flockFacebookService.prototype.decorateForMedia =
- function flockFacebookService_decorateForMedia(aDocument) {
- this._logger.debug(".decorateForMedia(aDocument)");
- var results = CC[HASH_PROPERTY_BAG_CONTRACTID]
- .createInstance(CI.nsIWritablePropertyBag2);
- var inst = this;
- if (this.webDetective.detect("facebook", "person", aDocument, results) ||
- this.webDetective.detect("facebook", "photo", aDocument, results)) {
- var userId = results.getPropertyAsAString("userid");
- var myListener = {
- onSuccess: function listener_onSuccess(aEnum) {
- aEnum = aEnum.QueryInterface(CI.nsISimpleEnumerator);
- var person = aEnum.getNext()
- .QueryInterface(CI.nsIWritablePropertyBag2);
- var username;
- try {
- username = person.getProperty("name");
- } catch (ex) {
- username = userId;
- }
-
- var mediaArr = [];
- var media = {
- name: username,
- query: "user:" + userId + "|username:" + username,
- label: username,
- favicon: inst.icon,
- service: inst.shortName
- }
- mediaArr.push(media);
- if (!aDocument._flock_decorations) {
- aDocument._flock_decorations = {};
- }
- aDocument._flock_decorations.mediaArr = mediaArr;
- inst.obs.notifyObservers(aDocument, "media", "media:update");
- }
- };
- this.api.usersGetInfo(myListener, userId, ["name"]);
- } else if (this.webDetective.detect("facebook",
- "tagged",
- aDocument,
- results))
- {
- var userId = results.getPropertyAsAString("userid");
- var myListener = {
- onSuccess: function listener_onSuccess(aEnum) {
- aEnum = aEnum.QueryInterface(CI.nsISimpleEnumerator);
- var person = aEnum.getNext()
- .QueryInterface(CI.nsIWritablePropertyBag2);
- var username;
- try {
- username = person.getProperty("name");
- } catch (ex) {
- username = userId;
- }
-
- var label = flockGetString("photo/mediabar",
- "flock.media.simpleviewing.searchunique",
- [username]);
-
- var mediaArr = [];
- var media = {
- name: username,
- query: "searchunique:" + userId + "|username:" + username,
- label: label,
- favicon: inst.icon,
- service: inst.shortName
- }
- mediaArr.push(media);
- if (!aDocument._flock_decorations) {
- aDocument._flock_decorations = {};
- }
- aDocument._flock_decorations.mediaArr = mediaArr;
- inst.obs.notifyObservers(aDocument, "media", "media:update");
- }
- };
- this.api.usersGetInfo(myListener, userId, ["name"]);
- }
- }
-
-
- // Checks if the TEXTAREA is drag and droppable
- flockFacebookService.prototype._isDnDableTextarea =
- function ffs__isDnDableTextarea(aDocument, aXPath, aTextarea) {
- if (aDocument && aXPath && aTextarea) {
- var xpath = this.webDetective.getString("facebook", aXPath, "");
- var results = aDocument.evaluate(xpath, aDocument, null,
- CI.nsIDOMXPathResult.ANY_TYPE, null);
- if (results && results.iterateNext() == aTextarea) {
- return true;
- }
- }
-
- return false;
- }
-
-
- // Set the focus on the Wall TEXTAREA by sending a focus event.
- // This action is only required to activate the field if the user has not yet
- // typed anything in the Wall space.
- flockFacebookService.prototype._setFocusOnWall =
- function ffs__setFocusOnWall(aXpathField, aTextarea) {
- if (aTextarea && aXpathField && aXpathField == XPATH_WALLTEXT) {
- var doc = aTextarea.ownerDocument;
- if (doc instanceof CI.nsIDOMHTMLDocument) {
- var placeholder = this.webDetective.getString("facebook",
- "wallPlaceholder",
- "");
-
- // If the placeholder text is displaying, trigger the focus event to
- // activate the TEXTAREA
- if (aTextarea.value == aTextarea.getAttribute(placeholder)) {
- var eventObj = doc.createEvent("Events");
- eventObj.initEvent("focus", true, true);
- aTextarea.dispatchEvent(eventObj);
- }
- }
- }
- }
-
-
- // BEGIN flockIRichContentDropHandler Interface
- // Handle the rich content drop
- flockFacebookService.prototype.handleDrop =
- function ffs_handleDrop(aFlavours, aTextarea) {
- this._logger.debug(".handleDrop()");
-
- var inst = this;
- var dropCallback = function facebook_dropCallback(aFlav) {
- var dataObj = {}, len = {};
- aFlavours.getTransferData(aFlav, dataObj, len);
-
- // Activate the TEXTAREA if we are dropping on the Wall
- inst._setFocusOnWall(XPATH_WALLTEXT, aTextarea);
-
- var caretPos = aTextarea.selectionEnd;
- var currentValue = aTextarea.value;
- // Add a trailing space so that Facebook scrapes for a valid,
- // unmodified url
- var nextChar = currentValue.charAt(caretPos);
- var trailingSpace = ((nextChar == "") ||
- (nextChar == " ") ||
- (nextChar == "\n"))
- ? ""
- : " ";
- // Only add a breadcrumb if the insertion point is at the end of
- // the text so that we don't duplicate breadcrumbs
- var breadcrumb = (aTextarea.value.length == caretPos)
- ? CC[FLOCK_RDNDS_CONTRACTID]
- .getService(CI.flockIRichDNDService)
- .getBreadcrumb("plain")
- : "";
-
- aTextarea.value = currentValue.substring(0, caretPos)
- + dataObj.value.QueryInterface(CI.nsISupportsString)
- .data.replace(/: /, ":\n")
- + trailingSpace
- + currentValue.substring(caretPos);
-
- // Call special Facebook hack to embed content into field
- _initKeyupEvent(aTextarea);
-
- aTextarea.value += breadcrumb;
- };
-
- return this._handleTextareaDrop(CATEGORY_ENTRY_NAME, this.fbService.domains,
- aTextarea, dropCallback);
- }
- // END flockIRichContentDropHandler interface
-
-
- //**************************************
- //* Facebook Account implementation
- //**************************************
-
- function flockFacebookAccount() {
- this.acUtils = CC[FLOCK_ACCOUNT_UTILS_CONTRACTID]
- .getService(CI.flockIAccountUtils);
- this.service = CC[FACEBOOK_CONTRACTID]
- .getService(CI.flockIWebService);
- this.api = CC["@flock.com/api/facebook;1"]
- .getService(CI.flockIFacebookAPI)
- .wrappedJSObject;
- this.faves_coop = CC[FLOCK_SINGLETON_CONTRACTID]
- .getService(CI.flockISingleton)
- .getSingleton(FAVES_COOP)
- .wrappedJSObject;
- this.webDetective = CC["@flock.com/web-detective;1"]
- .getService(CI.flockIWebDetective);
- this._ctk = {
- interfaces: [
- "nsISupports",
- "flockIWebServiceAccount",
- "flockISocialWebServiceAccount",
- "flockIFacebookAccount",
- "flockIMediaWebServiceAccount",
- "flockIMediaUploadAccount"
- ]
- };
- getCompTK().addAllInterfaces(this);
-
- this._logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
- this._logger.init("facebookAccount");
- }
-
- // BEGIN flockIWebServiceAccount interface
- flockFacebookAccount.prototype.urn = "";
- flockFacebookAccount.prototype.username = "";
- flockFacebookAccount.prototype.status = "";
-
- flockFacebookAccount.prototype.activate =
- function flockFacebookAccount_activate(aListener) {
- this._logger.debug(".activate()");
- var acctCoopObj = this.faves_coop.get(this.urn);
-
- var inst = this;
- this.api.authenticate({
- onSuccess: function onSuccess() {
- inst._logger.debug(".activate() authenticated: " + inst.api.uid);
- if (aListener) {
- // Get the user's full name before popping the mediabar so the stream
- // name is correct.
- var usersGetInfoListener = {
- onSuccess: function ugil_onSuccess(aEnum, aStatus) {
- aEnum.QueryInterface(CI.nsISimpleEnumerator);
- if (aEnum.hasMoreElements()) {
- var bag = aEnum.getNext();
- bag.QueryInterface(CI.nsIPropertyBag);
- acctCoopObj.avatar = bag.getProperty("avatar");
- acctCoopObj.name = bag.getProperty("name");
- acctCoopObj.nextRefresh = new Date(0);
- acctCoopObj.isPollable = true;
- acctCoopObj.isAuthenticated = true;
- aListener.onSuccess(inst, "accountAuthorized");
- }
- },
- onError: function ugil_onError() {
- inst._logger.error("Error on usersGetInfo");
- }
- };
- inst.api.usersGetInfo(usersGetInfoListener, inst.api.uid, null);
- }
- },
- onError: function onError(aSubject, aTopic, aError) {
- inst._logger.error("error on authentication: " + aError.errorString);
- }
- });
- }
-
- flockFacebookAccount.prototype.login =
- function flockFacebookAccount_login(aListener) {
- this._logger.debug(".login()");
- var inst = this;
- var myListener = {
- onSuccess: function listener_onSuccess() {
- inst.acUtils.ensureOnlyAuthenticatedAccount(inst.urn);
- // force refresh on login
- var pollerSvc = CC["@flock.com/poller-service;1"]
- .getService(CI.flockIPollerService);
- pollerSvc.forceRefresh(inst.urn);
- if (aListener) {
- aListener.onSuccess(null, "");
- }
- },
- onError: function listener_onError(aFlockError) {
- if (aListener) {
- aListener.onError(null, null, aFlockError);
- }
- }
- }
- this.api.deauthenticate();
- this.acUtils.markAllAccountsAsLoggedOut(FACEBOOK_CONTRACTID);
-
- this.api.authenticate(myListener);
- }
- // END flockIWebServiceAccount interface
-
- // BEGIN flockISocialWebServiceAccount interface
- flockFacebookAccount.prototype.hasFriendActions = true;
- flockFacebookAccount.prototype.isStatusSupported = true;
- flockFacebookAccount.prototype.isStatusEditable = true;
- flockFacebookAccount.prototype.isPostLinkSupported = true;
- flockFacebookAccount.prototype.isMyMediaFavoritesSupported = false;
-
- flockFacebookAccount.prototype.setStatus =
- function flockFacebookAccount_setStatus(aStatusMessage, aListener)
- {
- this._logger.info(".setStatus('" + aStatusMessage + "')");
-
- var inst = this;
-
- var statusListener = {
- onSuccess: function onSuccessHandler(aResult, aTopic) {
- inst.faves_coop.get(inst.urn).statusMessage = aStatusMessage;
- if (aListener) {
- aListener.onSuccess(aResult, aTopic);
- }
- },
- onError: function onErrorHandler(aError) {
- if (aListener) {
- aListener.onError(aError);
- }
- }
- }
-
- this.api.setStatus(aStatusMessage, statusListener);
- }
-
- flockFacebookAccount.prototype.getEditableStatus =
- function flockFacebookAccount_getEditableStatus()
- {
- this._logger.info(".getEditableStatus()");
- return this.faves_coop.get(this.urn).statusMessage;
- }
-
- flockFacebookAccount.prototype.formatStatusForDisplay =
- function flockFacebookAccount_formatStatusForDisplay(aStatusMessage)
- {
- var sbs = CC["@mozilla.org/intl/stringbundle;1"]
- .getService(CI.nsIStringBundleService);
- var bundle = sbs.createBundle(FACEBOOK_PROPERTIES);
- return bundle.GetStringFromName("flock.facebook.status.prefix") + " "
- + aStatusMessage
- + (!aStatusMessage.match(/[.!?]+$/)
- ? bundle.GetStringFromName("flock.facebook.status.postfix") : "");
- }
-
- flockFacebookAccount.prototype.getMeNotifications =
- function flockFacebookAccount_getMeNotifications()
- {
- this._logger.info(".getMeNotifications()");
-
- var sbs = CC["@mozilla.org/intl/stringbundle;1"]
- .getService(CI.nsIStringBundleService);
- var bundle = sbs.createBundle(FACEBOOK_PROPERTIES);
-
- var noties = [];
- var inst = this;
- function _addNotie(aType, aCount) {
- var stringName = "flock.facebook.noties."
- + aType + "."
- + ((parseInt(aCount) <= 0) ? "none" : "some");
- noties.push({
- class: aType,
- tooltip: bundle.GetStringFromName(stringName),
- metricsName: aType,
- count: aCount,
- URL: inst.webDetective.getString("facebook", aType + "_URL", "")
- });
- }
- var c_acct = this.faves_coop.get(this.urn);
- _addNotie("meMessages", c_acct.accountMessages);
- _addNotie("mePokes", c_acct.fbPokes);
- _addNotie("meFriendRequests", c_acct.fbFriendRequests);
- _addNotie("meGroupRequests", c_acct.fbGroupInvites);
- _addNotie("meEventRequests", c_acct.fbEventInvites);
- return JSON.toString(noties);
- }
-
- flockFacebookAccount.prototype.markAllMeNotificationsSeen =
- function flockFacebookAccount_markAllMeNotificationsSeen(aType) {
- this._logger.debug(".markAllMeNotificationsSeen('" + aType + "')");
- var c_acct = this.faves_coop.get(this.urn);
- switch (aType) {
- case "meMessages":
- c_acct.accountMessages = 0;
- break;
- case "mePokes":
- c_acct.fbPokes = 0;
- break;
- case "meFriendRequests":
- c_acct.fbFriendRequests = 0;
- break;
- case "meGroupRequests":
- c_acct.fbGroupInvites = 0;
- break;
- case "meEventRequests":
- c_acct.fbEventInvites = 0;
- break;
- default:
- break;
- }
- }
-
- flockFacebookAccount.prototype.getFriendActions =
- function flockFacebookAccount_getFriendActions(aFriendURN)
- {
- this._logger.info(".getFriendActions('" + aFriendURN + "')");
-
- var actionNames = ["friendPoke",
- "friendMessage",
- "friendWallPost",
- "friendShareLink",
- "friendViewProfile",
- "friendGiveGift",
- "friendShareFlock"];
-
- var sbs = CC["@mozilla.org/intl/stringbundle;1"]
- .getService(CI.nsIStringBundleService);
- var bundle = sbs.createBundle(FACEBOOK_PROPERTIES);
-
- var actions = [];
- var c_friend = this.faves_coop.get(aFriendURN);
- if (c_friend) {
- var c_acct = this.faves_coop.get(this.urn);
- for each (var i in actionNames) {
- actions.push({
- label: bundle.GetStringFromName("flock.facebook.actions." + i),
- class: i,
- spec: this.webDetective.getString("facebook", i, "")
- .replace("%accountid%", c_acct.accountId)
- .replace("%friendid%", c_friend.accountId)
- });
- }
- }
- return JSON.toString(actions);
- }
-
- flockFacebookAccount.prototype.getSharingAction =
- function flockFacebookAccount_getSharingAction(aFriendURN, aTransferable)
- {
- this._logger.info(".getSharingAction('" + aFriendURN + "'," +
- "'" + aTransferable + "')");
-
- // We want to take advantage of Facebook's "Share Link" feature so we try to
- // incorporate it for every object we share.
- return "method:flockIFacebookAccount:sendMessageWithSharedLink";
- }
-
- flockFacebookAccount.prototype.getPostLinkAction =
- function flockFacebookAccount_getPostLinkAction(aTransferable)
- {
- // We need to add the breadcrumb so we'll handle this in the postLink method
- return "method:flockIFacebookAccount:postLink";
- }
-
- flockFacebookAccount.prototype.getProfileURLForFriend =
- function flockFacebookAccount_getProfileURLForFriend(aFriendURN)
- {
- this._logger.info(".getProfileURLForFriend('" + aFriendURN + "')");
-
- var url = "";
- var c_friend = this.faves_coop.get(aFriendURN);
- if (c_friend) {
- url = this.webDetective.getString("facebook", "friendprofile", "")
- .replace("%accountid%", c_friend.accountId);
- }
-
- return url;
- }
-
- // END flockISocialWebServiceAccount interface
-
- // BEGIN flockIFacebookAccount interface
- flockFacebookAccount.prototype.sendMessageWithSharedLink =
- function flockFacebookAccount_sendMessageWithSharedLink(aFriendURN,
- aTransferable)
- {
- this._logger.info(".sendMessageWithSharedLink('" + aFriendURN + "'," +
- "'" + aTransferable + "')");
-
- var msgURL = "";
-
- if (!aTransferable) {
- // Was not a DND but a programmatical call
- // Special case requires us to get the active browser tab and then use
- // that as the sharing URL
- var win = _wm.getMostRecentWindow("navigator:browser");
- if (win) {
- var currentURL = win.gBrowser.currentURI.spec;
- var subject = win.gBrowser.contentTitle;
- var body = currentURL;
- var c_friend = this.faves_coop.get(aFriendURN);
-
- msgURL = this._buildSendMessageURL(c_friend.accountId, subject, body);
- }
- } else {
- msgURL = this._getSendMessageURLFromTransferable(aFriendURN,
- aTransferable);
- }
-
- if (msgURL) {
- this._loadNewTabWithCallback(msgURL, this._sharedLinkCallback);
- }
- }
-
- flockFacebookAccount.prototype.shareFlock =
- function flockFacebookAccount_shareFlock(aFriendURN)
- {
- this._logger.info(".shareFlock('" + aFriendURN + "')");
-
- // Get friend
- var c_friend = this.faves_coop.get(aFriendURN);
- if (c_friend) {
- // Get "Share Flock" message text
- var sbs = CC["@mozilla.org/intl/stringbundle;1"]
- .getService(CI.nsIStringBundleService);
- var bundle = sbs.createBundle(FACEBOOK_PROPERTIES);
- var subject = bundle.GetStringFromName(FACEBOOK_SHARE_FLOCK_SUBJECT);
- try {
- var body = bundle.GetStringFromName(FACEBOOK_SHARE_FLOCK_MESSAGE + "0");
- for (var i = 1; ; i++) {
- body += "\n" + bundle.GetStringFromName(FACEBOOK_SHARE_FLOCK_MESSAGE + i);
- }
- } catch (ex) {
- // Ignore -- we've hit the end of our message lines
- }
- // Build the URL and send it to the friend
- var msgURL = this._buildSendMessageURL(c_friend.accountId, subject, body);
- this._loadNewTabWithCallback(msgURL, this._shareFlockCallback);
- }
- }
-
- flockFacebookAccount.prototype.postLink =
- function flockFacebookAccount_postLink(aTransferable)
- {
- var url;
-
- if (aTransferable) {
- // Something was dropped onto the "Post Link" button: get the URL from the
- // transferable
-
- // This flavor set will ignore a text selection, which is OK
- var flavors = ["text/x-flock-media",
- "text/x-moz-url"];
- var message = CC[FLOCK_RDNDS_CONTRACTID]
- .getService(CI.flockIRichDNDService)
- .getMessageFromTransferable(aTransferable,
- flavors.length,
- flavors);
- url = message.body;
- } else {
- // The "Post Link" button was clicked: get the current tab's URL
- var win = _wm.getMostRecentWindow("navigator:browser");
- if (win) {
- url = win.gBrowser.currentURI.spec;
- }
- }
-
- if (url) {
- // Post it onto this user's profile
- var postURL = this.webDetective.getString("facebook", "postLink", "")
- .replace("%url%", encodeURIComponent(url));
- if (postURL) {
- this._loadNewTabWithCallback(postURL, this._postLinkCallback);
- }
- }
- }
-
- flockFacebookAccount.prototype._getSendMessageURLFromTransferable =
- function flockFacebookAccount__getSendMessageURLFromTransferable(aFriendURN,
- aTransferable)
- {
- this._logger.info("._getSendMessageURLFromTransferable('" + aFriendURN + "'," +
- "'" + aTransferable + "')");
-
- var messageURL = "";
- var c_friend = this.faves_coop.get(aFriendURN);
- if (c_friend) {
- // Flavors we want to support, in order of preference
- var flavors = ["text/x-flock-media",
- "text/x-moz-url",
- "text/unicode",
- "text/html"];
-
- var message = CC[FLOCK_RDNDS_CONTRACTID]
- .getService(CI.flockIRichDNDService)
- .getMessageFromTransferable(aTransferable,
- flavors.length,
- flavors);
- if (message.body) {
- messageURL = this._buildSendMessageURL(c_friend.accountId,
- message.subject,
- message.body);
- }
- }
-
- return messageURL;
- }
-
- flockFacebookAccount.prototype._buildSendMessageURL =
- function flockFacebookAccount__buildSendMessageURL(aFriendId, aSubject, aBody) {
- return this.webDetective.getString("facebook", "sendMessage", "")
- .replace("%friendid%", aFriendId)
- .replace("%subject%", encodeURIComponent(aSubject))
- .replace("%message%", encodeURIComponent(aBody));
- }
-
- // This opens a new tab and loads aURL into it. After the tab has loaded, the
- // aCallback method is called.
- flockFacebookAccount.prototype._loadNewTabWithCallback =
- function flockFacebookAccount__loadNewTabWithCallback(aUrl, aCallback)
- {
- var win = _wm.getMostRecentWindow("navigator:browser");
- if (win) {
- var browser = win.getBrowser();
- var newTab = browser.loadOneTab(aUrl, null, null, null, false, false);
-
- var obs = CC["@mozilla.org/observer-service;1"]
- .getService(CI.nsIObserverService);
-
- var observer = {
- observe: function loadNewTabWithCallback_observer(aContent,
- aTopic,
- aOwnsWeak)
- {
- var contentWindow = newTab.linkedBrowser.docShell
- .QueryInterface(CI.nsIInterfaceRequestor)
- .getInterface(CI.nsIDOMWindow);
-
- if (contentWindow == aContent) {
- obs.removeObserver(this, aTopic);
-
- // Callback?
- if (aCallback) {
- aCallback(contentWindow);
- }
- }
- }
- }
-
- obs.addObserver(observer, "EndDocumentLoad", false);
- }
- }
-
- // Callbacks for _loadNewTabWithCallback
-
- // Adds the breadcrumb to the post link comment field.
- flockFacebookAccount.prototype._postLinkCallback =
- function flockFacebookAccount__postLinkCallback(aWindow) {
- // TEXTAREA field on post link page is called "sharer_popup_message"
- var formField = aWindow.document.getElementById("sharer_popup_message");
- if (formField) {
- // Add breadcrumb
- _addBreadcrumb(formField);
-
- // Put the cursor at the top of the text area, above breadcrumb
- formField.setSelectionRange(0, 0);
- }
- }
-
- // Embeds the shared link into the content of the message and then adds the
- // breadcrumb.
- flockFacebookAccount.prototype._sharedLinkCallback =
- function flockFacebookAccount__sharedLinkCallback(aWindow) {
- // TEXTAREA field on send message page is called "message"
- var formField = aWindow.document.getElementById("message");
- if (formField) {
- // Call special Facebook hack to embed content into field
- _initKeyupEvent(formField);
-
- // Add breadcrumb
- _addBreadcrumb(formField);
- }
- }
-
- // Embeds the Flock link into the content of the message, cleans up the text,
- // and then moves the cursor to the top of the message.
- flockFacebookAccount.prototype._shareFlockCallback =
- function flockFacebookAccount__shareFlockCallback(aWindow) {
- // TEXTAREA field on send message page is called "message"
- var formField = aWindow.document.getElementById("message");
- if (formField) {
- // Call special Facebook hack to embed content into field
- _initKeyupEvent(formField);
-
- // Facebook converts newlines on the command line to breaks in the message,
- // which looks bad, so convert them back to newlines in the message.
- formField.value = formField.value.replace("<br />", "\n", "gi");
-
- // Put the cursor at the top of the text area, above our footer
- formField.setSelectionRange(0, 0);
- }
- }
-
- // END flockIFacebookAccount interface
-
- //******************************************
- //* XPCOM registration
- //******************************************
-
- function createModule(aParams) {
- return {
- registerSelf: function registerSelf(aCompMgr,
- aFileSpec,
- aLocation,
- aType) {
- aCompMgr.QueryInterface(CI.nsIComponentRegistrar);
- aCompMgr.registerFactoryLocation(aParams.CID,
- aParams.componentName,
- aParams.contractID,
- aFileSpec,
- aLocation,
- aType);
- var catMgr = CC[CATEGORY_MANAGER_CONTRACTID].
- getService(CI.nsICategoryManager);
- if (!aParams.categories) {
- aParams.categories = [];
- }
- for (var i = 0; i < aParams.categories.length; i++) {
- var cat = aParams.categories[i];
- catMgr.addCategoryEntry(cat.category,
- cat.entry,
- cat.value,
- true,
- true);
- }
- },
- getClassObject: function getClassObject(aCompMgr, aCID, aIID) {
- if (!aCID.equals(aParams.CID)) {
- throw CR.NS_ERROR_NO_INTERFACE;
- }
- if (!aIID.equals(CI.nsIFactory)) {
- throw CR.NS_ERROR_NOT_IMPLEMENTED;
- }
- return { // Factory
- createInstance: function createInstance(aOuter, aIID) {
- if (aOuter != null) {
- throw CR.NS_ERROR_NO_AGGREGATION;
- }
- var comp = new aParams.componentClass();
- if (aParams.implementationFunc) {
- aParams.implementationFunc(comp);
- }
- return comp.QueryInterface(aIID);
- }
- };
- },
- canUnload: function canUnload(aCompMgr) {
- return true;
- }
- };
- }
-
- // NS Module entrypoint
- function NSGetModule(aCompMgr, aFileSpec) {
- return createModule({
- componentClass: flockFacebookService,
- CID: FACEBOOK_CID,
- contractID: FACEBOOK_CONTRACTID,
- componentName: CATEGORY_COMPONENT_NAME,
- implementationFunc: function implementationFunc(aComp) {
- getCompTK().addAllInterfaces(aComp);
- },
- categories: [
- {
- category: "wsm-startup",
- entry: CATEGORY_COMPONENT_NAME,
- value: FACEBOOK_CONTRACTID
- },
- {
- category: "flockWebService",
- entry: CATEGORY_ENTRY_NAME,
- value: FACEBOOK_CONTRACTID
- },
- {
- category: "flockMediaProvider",
- entry: CATEGORY_ENTRY_NAME,
- value: FACEBOOK_CONTRACTID
- },
- {
- category: FLOCK_RICH_CONTENT_CATEGORY_ENTRY,
- entry: CATEGORY_ENTRY_NAME,
- value: FACEBOOK_CONTRACTID
- }
- ]
- });
- }
-